package com.itheima.esdemo;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.itheima.mapper.GoodsMapper;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpHost;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.indices.CreateIndexRequest;
import org.elasticsearch.client.indices.CreateIndexResponse;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.common.unit.Fuzziness;
import org.elasticsearch.common.xcontent.XContentType;
import com.itheima.pojo.Good;
import org.elasticsearch.index.query.*;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortOrder;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;

import javax.annotation.Resource;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;

/**
 * @author YYP
 * @date 2020/12/16
 */
@SpringBootTest
public class GoodTest {

    @Resource
    private GoodsMapper mapper;

    RestHighLevelClient client;

    /**
     * 创建连接
     */
    @BeforeEach
    public void test() {
        client = new RestHighLevelClient(
                RestClient.builder(
                        new HttpHost("192.168.200.150", 9200, "http")
                )

        );
        System.out.println(client);
    }

    /**
     * 创建索引库，并且映射
     *
     * @throws IOException
     */
    @Test
    public void testCerateIndex() throws IOException {
        //创建CreateIndexRequest对象，并指定索引库名称
        CreateIndexRequest request = new CreateIndexRequest("goods");
        HashMap<String, Object> map = new HashMap<>();

        //指定settings配置
        request.settings(Settings.builder()
                .put("index.number_of_shards", 3)
                .put("index.number_of_replicas", 2)
        );

        //指定mapping配置
        request.mapping(
                "{\n" +
                        "    \"properties\": {\n" +
                        "      \"id\":{\n" +
                        "        \"type\": \"keyword\"\n" +
                        "      },\n" +
                        "      \"name\":{\n" +
                        "        \"type\": \"text\",\n" +
                        "        \"analyzer\": \"ik_max_word\"\n" +
                        "      },\n" +
                        "      \"price\":{\n" +
                        "        \"type\": \"integer\"\n" +
                        "      },\n" +
                        "      \"num\":{\n" +
                        "        \"type\": \"integer\"\n" +
                        "      },\n" +
                        "      \"image\":{\n" +
                        "        \"type\": \"keyword\",\n" +
                        "        \"index\": false\n" +
                        "      },\n" +
                        "       \"images\":{\n" +
                        "        \"type\": \"keyword\",\n" +
                        "        \"index\": false\n" +
                        "      },\n" +
                        "      \"create_time\":{\n" +
                        "        \"type\": \"date\",\n" +
                        "        \"index\": false\n" +
                        "      },\n" +
                        "      \"category_id\":{\n" +
                        "        \"type\": \"integer\"\n" +
                        "      },\n" +
                        "      \"category_name\":{\n" +
                        "        \"type\": \"text\"\n" +
                        "      },\n" +
                        "      \"brand_name\":{\n" +
                        "        \"type\": \"text\"\n" +
                        "      }\n" +
                        "    }\n" +
                        "  }",
                //指定映射的内容的类型为json
                XContentType.JSON);

        //发起请求，得到响应
        CreateIndexResponse response = client.indices().create(request, RequestOptions.DEFAULT);
        //打印结果
        System.out.println("response = " + response.isAcknowledged());
    }

/*    @Test
    public void insert() throws IOException {
        //1. 准备文档数据
        Good good = new Good();
        //2. 创建IndexRequest对象，并指定索引库名称
        IndexRequest indexRequest = new IndexRequest("goods");
        //3. 指定新增的数据的id
        indexRequest.id(good.getId().toString() );
        //4. 将新增的文档数据变成JSON格式
        String userJson = JSON.toJSONString(good);
        //5. 将JSON数据添加到IndexRequest中
        indexRequest.source(userJson,XContentType.JSON);
        //6. 发起请求，得到结果
        IndexResponse response = client.index(indexRequest, RequestOptions.DEFAULT);
        System.out.println("response.getResult() = " + response.getResult());
    }*/

/*    @Test
    public void testfindDocumentById() throws Exception{
        //1. 创建GetRequest 对象，并指定索引库名称、文档ID
        GetRequest getRequest = new GetRequest("user", "110");
        //2. 发起请求，得到结果
        GetResponse response = client.get(getRequest, RequestOptions.DEFAULT);
        //3. 从结果中得到source，是json字符串
        String sourceAsString = response.getSourceAsString();
        //4. 将JSON反序列化为对象
        User user = JSON.parseObject(sourceAsString, User.class);
        System.out.println("user = " + user);
    }*/

    /**
     * 根据id删除文档
     * @throws IOException
     */
/*    @Test
    public void testDeleteDocumentById() throws IOException {
        //1. 创建DeleteRequest对象，指定索引库名称、文档ID
        DeleteRequest request = new DeleteRequest("goods", "");
        //2. 发起请求
        DeleteResponse deleteResponse = client.delete(request, RequestOptions.DEFAULT);
        System.out.println("deleteResponse = " + deleteResponse.getResult());
    }*/

    /**
     * 大量数据批量添加
     *
     * @throws IOException
     */
    @Test
    public void testBulkAddDocumentList() throws IOException {
        //1. 从数据库查询文档数据
        //QueryWrapper<Good> wrapper = new QueryWrapper<>();
//        QueryWrapper<Good> wrapper = new QueryWrapper<>();
//        List<Good> goods = mapper.selectList(wrapper);

        Page<Good> page = new Page<>(1, 5000);
        Page<Good> pageResult = mapper.selectPage(page, null);
        long totalPages = pageResult.getPages();
        if (totalPages > 0 && page != null) {
            //遍历页码批量插入到索引库
            //2. 创建BulkRequest对象
            BulkRequest bulkRequest = new BulkRequest("goods");
            for (long i = 1; i < totalPages; i++) {
                if (i > 1) {
                    page = new Page<>(i, 5000);
                    pageResult = mapper.selectPage(page, null);
                }
                //3. 创建多个IndexRequest对象，组织文档数据，并添加到BulkRequest中
                List<Good> goods = pageResult.getRecords();
                for (Good good : goods) {
                    IndexRequest indexRequest = new IndexRequest();
                    //指定新增的id
                    indexRequest.id(good.getId() + "");
                    //将新增的文档数据变成sjon 格式
                    String goodJson = JSON.toJSONString(good);
                    //将json数据添加到indexrequest中
                    indexRequest.source(goodJson, XContentType.JSON);
                    //将indexrequest添加到bulkRequest中
                    bulkRequest.add(indexRequest);
                }
                //4. 发起请求
                BulkResponse bulkResponse = client.bulk(bulkRequest, RequestOptions.DEFAULT);
                System.out.println("bulkResponse = " + bulkResponse.status());
            }
        }
    }


    /**
     * 查询所有
     */
    @Test
    public void testMatchAll() throws IOException {
        //1.创建SearchSourceBuilder对象
        SearchSourceBuilder builder = new SearchSourceBuilder();
        //1.1添加查询条件QueryBuilders，这里选择match_all，查询所有
        QueryBuilder matchAllQuery = QueryBuilders.matchAllQuery();
        builder.query(matchAllQuery);
        //1.2添加排序、分页等其他条件（忽略）

        //2.创建SearchRequest对象，并制定索引库名称
        SearchRequest request = new SearchRequest("goods");
        // 3.添加SearchSourceBuilder对象到SearchRequest对象中
        request.source(builder);

        // 4.发起请求，得到结果
        SearchResponse searchResponse = client.search(request, RequestOptions.DEFAULT);
        // 5.解析结果
        SearchHits searchHits = searchResponse.getHits();
        //  5.1.获取总条数
        System.out.println("total:" + searchHits.getTotalHits().value);
        //  5.2.获取SearchHit数组，并遍历
        SearchHit[] hits = searchHits.getHits();
        for (SearchHit hit : hits) {
            //  - 获取其中的`_source`，是JSON数据
            String goodsJsonStr = hit.getSourceAsString();
            //  - 把`_source`反序列化为Good对象
            Good good = JSON.parseObject(goodsJsonStr, Good.class);
            System.out.println(good);
        }

    }

    /**
     * 词条查询termQuery-不分词
     */
    @Test
    public void testTermQuery() throws Exception {
        //1. 创建SearchSourceBuilder对象
        SearchSourceBuilder builder = new SearchSourceBuilder();
        //   1. 添加查询条件QueryBuilders.termQuery()
        TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("name", "夕阳红");
        builder.query(termQueryBuilder);
        //2. 创建SearchRequest对象，并制定索引库名称
        SearchRequest request = new SearchRequest("goods");
        //3. 添加SearchSourceBuilder对象到SearchRequest对象source中
        request.source(builder);
        //4. 发起请求，得到结果
        SearchResponse response = client.search(request, RequestOptions.DEFAULT);
        //5. 解析结果SearchResponse
        SearchHits searchHits = response.getHits();
        //1. 获取总条数
        System.out.println(searchHits.getTotalHits().value);
        //2. 获取SearchHits数组，并遍历
        for (SearchHit searchHit : searchHits) {
            //        * 获取其中的 `_source`，是JSON数据
            String goodJson = searchHit.getSourceAsString();
            //        * 把 `_source` 反序列化为User对象
            Good good = JSON.parseObject(goodJson, Good.class);
            System.out.println(good);
        }

    }

    /**
     * 抽取通用构建查询条件执行查询方法
     *
     * @throws Exception
     */
    public void printResultByQuery(QueryBuilder queryBuilder) throws IOException {
        //1.创建Builder对象
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        //构建查询条件
        sourceBuilder.query(queryBuilder);

//-----------------------------------------------------
        //1.2添加排序、分页等其他条件（）
        sourceBuilder.sort("price", SortOrder.ASC);
        //设置分页
        int page = 2;
        int size = 2;
        int from = (page - 1) * size;
        sourceBuilder.from(from);
        sourceBuilder.size(size);
//-------------------------------------------------------------
        // ***** 设置高亮三要素
        HighlightBuilder highlight = SearchSourceBuilder.highlight();
        highlight.field("name");  // 高亮显示域
        highlight.preTags("<font color='red'>"); // 高亮显示前缀
        highlight.postTags("</font>");  // 高亮显示后缀
        sourceBuilder.highlighter(highlight);

//---------------------------------------------------------------
        //2.创建SearchRequest对象，并制定索引库
        SearchRequest searchRequest = new SearchRequest("goods");
        //3.添加Builder到request的source中
        searchRequest.source(sourceBuilder);
        //4.发起请求
        SearchResponse response = client.search(searchRequest, RequestOptions.DEFAULT);
        //5.解析结果
        SearchHits hits = response.getHits();
        System.out.println(hits.getTotalHits().value);
        for (SearchHit hit : hits) {
            //转换为string
            String goodJson = hit.getSourceAsString();
            //将json字符串转换为对象
            Good good = JSON.parseObject(goodJson, Good.class);

            //---------------------------
            // ***** 解析高亮数据
            HighlightField highlightField = hit.getHighlightFields().get("name");
            // get("高亮显示域名称")
            Text[] fragments = highlightField.getFragments();
            String name = StringUtils.join(fragments);
            // 判断如果是可以获取到数据则更新到商品对象中
            if (StringUtils.isNotBlank(name)) {
                good.setName(name);
            }
            //----------------------------


            System.out.println(good);
        }
    }

    /**
     * 匹配查询MatchQuery 对条件进行分词
     *
     * @throws Exception
     */
    @Test
    public void testMatchQuery() throws Exception {
        MatchQueryBuilder queryBuilder = QueryBuilders.matchQuery("name", "夕阳红");
        queryBuilder.analyzer("ik_max_word");
        queryBuilder.operator(Operator.OR);
        printResultByQuery(queryBuilder);
    }

    /**
     * 模糊查询FuzzyQuery 对查询条件可以修正
     *
     * @throws Exception
     */
    @Test
    public void testFuzzyQuery() throws Exception {
        FuzzyQueryBuilder queryBuilder = QueryBuilders.fuzzyQuery("brandName", "夕阳红");
        queryBuilder.fuzziness(Fuzziness.TWO);
        printResultByQuery(queryBuilder);
    }

    /**
     * 条件查询 + 排序
     *
     * @throws Exception
     */
    @Test
    public void testRangeQuery() throws Exception {
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        RangeQueryBuilder queryBuilder = QueryBuilders.rangeQuery("price")
                .gte(101)
                .lte(899);//    101 <= price <=899
        //构建查询条件
        sourceBuilder.query(queryBuilder);
        sourceBuilder.sort("price", SortOrder.ASC);

        SearchRequest request = new SearchRequest("goods");
        request.source(sourceBuilder);
        SearchResponse response = client.search(request, RequestOptions.DEFAULT);
        SearchHits responseHits = response.getHits();
        System.out.println(responseHits.getTotalHits().value);

        for (SearchHit responseHit : responseHits) {
            String goodJson = responseHit.getSourceAsString();
            // System.out.println(goodJson);
            Good good = JSON.parseObject(goodJson, Good.class);
            System.out.println(good);
        }
    }

    /**
     * QueryString 多条件查询
     *
     * @throws Exception
     */
    @Test
    public void testQueryStringQuery() throws Exception {
        QueryStringQueryBuilder queryBuilder = QueryBuilders.queryStringQuery("黑马同学")
                .field("name")
                .field("brandName")
                .defaultOperator(Operator.OR);
        printResultByQuery(queryBuilder);
    }

    /**
     * 匹配查询BoolQuery 布尔查询+过滤
     *
     * @throws Exception
     */
    @Test
    public void testBoolQuery() throws Exception {
        //1.构建bool条件对象
        BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery();
        //2.构建matchQuery对象，
        MatchQueryBuilder matchQueryBuilder = QueryBuilders.matchQuery("name", "黑鲨");
        queryBuilder.must(matchQueryBuilder);
        //3.过滤
        TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("name", "游戏");
        queryBuilder.filter(termQueryBuilder);

        //4.过滤价格
        RangeQueryBuilder rangeQueryBuilder = QueryBuilders.rangeQuery("price")
                .gte(39500)
                .lt(40000);
        queryBuilder.filter(rangeQueryBuilder);

        printResultByQuery(queryBuilder);
    }

    /**
     * 布尔查询 分页
     *
     * @throws Exception
     */
    @Test
    public void testBoolQueryByPage() throws Exception {
        //构建bool对象
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();

        //构建matchQuery对象
        MatchQueryBuilder matchQueryBuilder = QueryBuilders.matchQuery("name", "鲨");
        boolQueryBuilder.must(matchQueryBuilder);

        printResultByQuery(boolQueryBuilder);
    }


    @AfterEach
    public void close() {
        if (client != null) {
            try {
                client.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
